Quickstart: Building a product listing page with Search.js
Introduction
While the Luigi's Box API provides maximum control, the Search.js library offers the fastest path to a feature-rich and fully interactive Product Listing Page (PLP). By using Search.js for listings, you can render a complete Product listing page, including dynamic filters, sorting, and pagination, with just a few lines of JavaScript. This guide will show you how to correctly initialize the Search.js widget and then execute a listing for a specific category, effectively turning the search UI into a powerful category page.
What you'll learn
- How to include and initialize the Search.js library with essential configurations.
- How to execute a product listing for a specific category using Search.js.
Who is this guide for
- Developers looking for a fast, out-of-the-box solution for a product listing page.
Prerequisites
- Basic knowledge of HTML and JavaScript.
- Your Luigi's Box
TrackerId
.
Step-by-step: Building the product listing page
Step 1: Create a new product listing page
First, you need to add two key elements to your HTML page: an <input>
for the search bar and a <div>
that will act as a container for the entire Product listing user interface.
Search.js requires a search input for initialization, even if the primary purpose of the page is to display a category listing.
Example: plp.html
body
<body>
<h1>Product Listing</h1>
<!-- A search input is required for Search.js to initialize -->
<input type="search" id="search-input" placeholder="Search within our products...">
<!-- The container where the Product Listing UI will be rendered -->
<div id="plp-ui-container">
<p>Loading products...</p>
</div>
<!-- The Search.js library will be added in the next step -->
<script src="https://cdn.luigisbox.com/search.js"></script>
<script>
// The initialization script will go here
</script>
</body>
</html>
Step 2: Initialize and execute the listing
A key aspect of using Search.js for product listings is understanding its two-step process:
-
Initialization (
Luigis.Search({...})
): The first call you make is toLuigis.Search()
. Its job is to initialize the entire search interface. It sets up all the components like facets and sorting and links the library to your HTML placeholders. At this stage, the UI is ready, but no search has been performed. -
Execution (
Luigis.Search.Search(...)
): The second call you make is to theSearch()
method on the already-initializedLuigis.Search
object. Its job is to execute the initial search. For a listing page, you'll execute this with anull
query but with a specialIntentFilters
object that tells the system what category to display.
Add the following script to your HTML file, just before the closing </body>
tag.
Example: Initialization and execution script
<script>
document.addEventListener('DOMContentLoaded', function() {
// 1. First, INITIALIZE the search UI.
Luigis.Search({
TrackerId: '111111-111111', // Replace with your Tracker ID
Theme: 'boo',
Facets: ['brand', 'price_amount'],
DefaultFilters: {
type: 'product'
}
},
'#search-input',
'#plp-ui-container'
);
// 2. Immediately after, EXECUTE the search for the category.
Luigis.Search.Search(
null, // Query is null for a listing
{
ProductListingFilter: "category",
IntentFilters: {
category: "Guitars"
}
}
);
});
</script>
This script does the following:
- Initializes the Search.js library with your
Tracker ID
and sets the theme to'boo'
. - Sets the facets to be displayed (e.g., brand and price).
- Defines the default filter to only show products.
- Executes the search with a
null
query, specifying that you want to filter by the "Guitars" category.
Step 3: Handling multiple product listing pages
The example above is perfect for a single page. But what if your website has many standalone HTML files for different brands and categories (e.g., yamaha.html
, guitars.html
)? You need a scalable approach.
The best practice is to create a single, reusable JavaScript file that can intelligently configure itself based on which page it's on, using HTML data-
attributes.
Example: Brand product listing page (yamaha.html
) HTML setup
<!DOCTYPE html>
<html lang="en">
<body data-plp-type="brand" data-plp-value="Yamaha">
<h1>Yamaha Products</h1>
<input type="search" id="search-input" style="display: none;" />
<div id="plp-ui-container">Loading...</div>
<!-- Load the libraries -->
<script src="https://cdn.luigisbox.com/search.js"></script>
<!-- Load YOUR reusable script -->
<script src="/scripts/plp-loader.js"></script>
</body>
</html>
Example: Category product listing page (guitars.html
) HTML setup
<!DOCTYPE html>
<html lang="en">
<body data-plp-type="category" data-plp-value="Guitars">
<h1>Guitar Products</h1>
<input type="search" id="search-input" style="display: none;" />
<div id="plp-ui-container">Loading...</div>
<!-- Load the libraries -->
<script src="https://cdn.luigisbox.com/search.js"></script>
<!-- Load YOUR reusable script -->
<script src="/scripts/plp-loader.js"></script>
</body>
</html>
Example: Reusable product listing loader script (plp-loader.js
)
Now, create one JavaScript file that will be included on every product listing page. This script reads the data-
attributes from the body and configures Search.js dynamically.
// File: /scripts/plp-loader.js
document.addEventListener('DOMContentLoaded', function() {
const body = document.body;
const filterType = body.dataset.plpType; // Reads "data-plp-type"
const filterValue = body.dataset.plpValue; // Reads "data-plp-value"
// If the required data attributes aren't on this page, do nothing.
if (!filterType || !filterValue) {
return;
}
// 1. Initialize the UI (same configuration for all pages)
Luigis.Search({
TrackerId: '111111-111111', // Your Tracker ID
Theme: 'boo',
Facets: ['brand', 'price_amount'],
DefaultFilters: {
type: 'product'
}
},
'#search-input',
'#plp-ui-container'
);
// 2. Execute the listing using the dynamic values from the HTML
Luigis.Search.Search(
null,
{
ProductListingFilter: filterType,
// Use the dynamic values to build the IntentFilters object
IntentFilters: {
[filterType]: filterValue
}
}
);
});
Best practices
-
Match the
ProductListingFilter
: Ensure the value ofProductListingFilter
exactly matches the key you are using inIntentFilters
. This is how Luigi's Box applies your merchandising rules, and a mismatch is a common source of errors. -
Use a theme: The
Theme: 'boo'
setting is the recommended way to apply modern, responsive styling to the widget. It provides a solid foundation that you can then customize with your own CSS or by overriding the default templates. -
Plan for a universal search bar: While the multi-page example requires an
<input>
on each page, the ideal architecture for a real website is a shared layout or header that contains a single, universal search bar. This provides a more consistent user experience and simplifies your code. - Customize with templates: For full control over the look and feel, Search.js offers a powerful templating system. You can override the default HTML of any component, from the product tiles to the facet filters.
Next steps
Now that you have a working and scalable Product listing page, you can customize its appearance and explore more advanced features.